#include <perft.h>

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include <position.h>
#include <game.h>
#include <movegen.h>
#include <move.h>
#include <san.h>

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#define BUFLEN 64


/* Used by divide to sort the moves. */
static int movecmp(const void* a, const void* b)
{
  char bufa[6];
  char bufb[6];

  move_to_string(*((const move_t*)a), bufa);
  move_to_string(*((const move_t*)b), bufb);

  return strcmp(bufa, bufb);
}


uint64_t perft_position(struct game_t* g, int depth)
{
  assert(g != 0);
  assert(depth >= 0);

  if (depth == 0)
    return 1;

  const position_t* pos = game_cur_position(g);
  
  move_t buf[2 * MAX_N_MOVES];
  int n = movegen_legal_moves(pos, buf);
  if (n > MAX_N_MOVES)
    fprintf(stderr, "Warning: generated more than %d moves.\n", n);

  if (depth == 1)
    return n;

  uint64_t nodes = 0;
  for (int i = 0; i < n; i++) {
    hash_key_t key = position_get_hash_key(pos);
    hash_key_t tag = position_get_hash_tag(pos);
    hash_key_t pkey = position_get_pawn_hash_key(pos);
    assert(game_in_check(g)
	   ? movegen_detect_check(pos) != 0
	   : movegen_detect_check(pos) == 0);
    game_make_move(g, buf[i]);
    nodes += perft_position(g, depth - 1);
    game_undo_move(g);

    assert(key == position_get_hash_key(pos));
    assert(tag == position_get_hash_tag(pos));
    assert(pkey == position_get_pawn_hash_key(pos));
    if (key != position_get_hash_key(pos))
      fprintf(stderr, "Warning: corrupted hash key.\n");
    if (tag != position_get_hash_tag(pos))
      fprintf(stderr, "Warning: corrupted hash tag.\n");
    if (pkey != position_get_pawn_hash_key(pos))
      fprintf(stderr, "Warning: corrupted pawn hash key.\n");
  }

  return nodes;
}


void divide_position(struct game_t* g, int depth)
{
  assert(g != 0);
  assert(depth > 0);

  move_t buf[2 * MAX_N_MOVES];
  const position_t* pos = game_cur_position(g);
  int n = movegen_legal_moves(pos, buf);
  if (n > MAX_N_MOVES)
    fprintf(stderr, "Warning: generated more than %d moves.\n", n);

  qsort(buf, n, sizeof(move_t), movecmp);
  
  printf("%d legal moves\n", n);
  char s[6];
  for (int i = 0; i < n; i++) {
    move_to_string(buf[i], s);
    if (depth > 1) {
      printf("%s ", s);
      fflush(stdout);
      game_make_move(g, buf[i]);
      uint64_t nodes = perft_position(g, depth - 1);
      game_undo_move(g);
      printf("%" PRIu64 "\n", nodes);
    } else {
      printf("%s\n", s);
    }
  }
}
